--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 997aae822c332b5a706eca1f8e3b7dc670711be6
Parents : 34ef730
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-29T14:36:18-05:00
chore(docker): update Dockerfile to include script for handling musl filterlib artifacts and adjust Python hash
Changes
2 files changed, 87 insertions(+), 4 deletions(-)
Diff
diff --git a/Dockerfile b/Dockerfile
index ef506b03..6e324e3f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,12 +2,16 @@
# 1. build-frontend: Build static frontend assets using Node
# 2. builder: Install Python dependencies, build and collect backend files in a venv
# 3. final image: Copy venv, install runtime deps, set up container user and config
+#
+# LXST wheels ship glibc-tagged filterlib extensions only. On Alpine/musl, cffi
+# compiles at build time; scripts/docker-bake-lxst-filterlib-musl.py copies the
+# artifact to the import name LXST.filterlib so runtime does not need gcc.
# ---- Global Build Args ----
ARG NODE_IMAGE=node:24-alpine
ARG NODE_HASH=sha256:0340fa682d72068edf603c305bfbc10e23219fb0e40df58d9ea4d6f33a9798bf
ARG PYTHON_IMAGE=python:3.14.4-alpine3.23
-ARG PYTHON_HASH=sha256:27ac3ba1699f7a526ad19bf0d35c12369b43d3439e08297a880398d97899c3d8
+ARG PYTHON_HASH=sha256:dd4d2bd5b53d9b25a51da13addf2be586beebd5387e289e798e4083d94ca837a
# ---- STAGE 1: Frontend Build ----
FROM ${NODE_IMAGE}@${NODE_HASH} AS build-frontend
@@ -35,7 +39,7 @@ RUN pip install --no-cache-dir --upgrade "pip>=26.0" poetry setuptools wheel "ja
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
-# Install essential runtime tools in the venv
+# Install essential runtime tools in the venv (cffi verify needs setuptools on Python 3.12+)
RUN pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.context>=6.1.0"
COPY pyproject.toml poetry.lock README.md ./
@@ -45,11 +49,11 @@ RUN poetry config virtualenvs.create false && \
rm -rf /root/.cache/pip /root/.cache/pypoetry
COPY meshchatx ./meshchatx
+COPY scripts/docker-bake-lxst-filterlib-musl.py ./scripts/docker-bake-lxst-filterlib-musl.py
COPY --from=build-frontend /src/meshchatx/public ./meshchatx/public
RUN pip install --no-cache-dir . && \
- python -c "import LXST.Filters; print('LXST Filters compiled successfully')" && \
- # Remove unnecessary files from the venv
+ python scripts/docker-bake-lxst-filterlib-musl.py && \
find /opt/venv -type d -name "tests" -exec rm -rf {} + && \
find /opt/venv -type d -name "test" -exec rm -rf {} + && \
find /opt/venv -type d -name "__pycache__" -exec rm -rf {} + && \
diff --git a/scripts/docker-bake-lxst-filterlib-musl.py b/scripts/docker-bake-lxst-filterlib-musl.py
new file mode 100644
index 00000000..7d6abbfb
--- /dev/null
+++ b/scripts/docker-bake-lxst-filterlib-musl.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: 0BSD
+
+"""Alpine/musl Docker: copy cffi-built filter shared library to LXST.filterlib name.
+
+LXST ships glibc-tagged ``filterlib*.so`` wheels; musl ignores them and cffi
+``verify()`` drops the musl artifact under ``LXST/__pycache__/_cffi__*.so``.
+Without this step, a fresh process cannot resolve ``LXST.filterlib`` for
+``ffi.dlopen()`` and would try to compile again at runtime (no gcc).
+
+The cffi artifact is a plain shared library (loaded via dlopen), not a Python
+extension module (no ``PyInit_filterlib``); do not ``import LXST.filterlib``.
+"""
+
+from __future__ import annotations
+
+import importlib.util
+import shutil
+import subprocess
+import sys
+import sysconfig
+from pathlib import Path
+
+
+def main() -> int:
+ import LXST
+
+ pkg = Path(LXST.__file__).resolve().parent
+ ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") or ""
+ target = pkg / f"filterlib{ext_suffix}"
+
+ import LXST.Filters # noqa: F401 — triggers cffi verify when needed
+
+ candidates = sorted(
+ pkg.glob("__pycache__/_cffi__*.cpython-*-linux-musl.so"),
+ key=lambda p: p.stat().st_mtime,
+ reverse=True,
+ )
+ if not candidates:
+ print(
+ "docker-bake-lxst-filterlib-musl: no musl _cffi shared library under "
+ "site-packages/LXST/__pycache__ (did Filters import compile?)",
+ file=sys.stderr,
+ )
+ return 1
+
+ src = candidates[0]
+ shutil.copy2(src, target)
+
+ spec = importlib.util.find_spec("LXST.filterlib")
+ if not spec or not spec.origin:
+ print(
+ "docker-bake-lxst-filterlib-musl: LXST.filterlib still not discoverable "
+ f"after copying {src.name}",
+ file=sys.stderr,
+ )
+ return 1
+
+ verify = subprocess.run(
+ [
+ sys.executable,
+ "-c",
+ "import LXST.Filters as F; raise SystemExit(0 if F.USE_NATIVE_FILTERS else 1)",
+ ],
+ check=False,
+ )
+ if verify.returncode != 0:
+ print(
+ "docker-bake-lxst-filterlib-musl: fresh interpreter did not load native filters",
+ file=sys.stderr,
+ )
+ return 1
+
+ print(f"docker-bake-lxst-filterlib-musl: OK ({src.name} -> {target.name})")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────